package github.saw96x.remoting.transport.netty.server;

import github.saw96x.common.factory.SingletonFactory;
import github.saw96x.common.utils.RuntimeUtil;
import github.saw96x.common.utils.threadpool.ThreadPoolFactoryUtil;
import github.saw96x.config.CustomShutdownHook;
import github.saw96x.config.RpcServiceConfig;
import github.saw96x.provider.Impl.ZkServiceProviderImpl;
import github.saw96x.provider.ServiceProvider;
import github.saw96x.remoting.transport.netty.codec.RpcMessageDecoder;
import github.saw96x.remoting.transport.netty.codec.RpcMessageEncoder;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.net.InetAddress;
import java.util.concurrent.TimeUnit;

/**
 * @author Saw96x
 * @date 2022/3/18 9:51
 */
@Slf4j
@Component
public class NettyRpcServer {

  public static final int PORT = 9998;

  private final ServiceProvider serviceProvider = SingletonFactory.getInstance(ZkServiceProviderImpl.class);

  public void registerService(RpcServiceConfig rpcServiceConfig) {
    serviceProvider.publishService(rpcServiceConfig);
  }

  @SneakyThrows
  public void start() {
    CustomShutdownHook.getCustomShutdownHook().clearAll();
    String host = InetAddress.getLocalHost().getHostAddress();
    EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    EventLoopGroup workerGroup = new NioEventLoopGroup();
    DefaultEventExecutorGroup serviceHandlerGroup = new DefaultEventExecutorGroup(
        RuntimeUtil.cpus() * 2,
        ThreadPoolFactoryUtil.createThreadFactory("service-handler-group", false)
    );
    try {
      ServerBootstrap b = new ServerBootstrap();
      b.group(bossGroup, workerGroup)
          .channel(NioServerSocketChannel.class)
          // TCP默认开启了 Nagle 算法，该算法的作用是尽可能的发送大数据快，减少网络传输。TCP_NODELAY 参数的作用就是控制是否启用 Nagle 算法。
          .childOption(ChannelOption.TCP_NODELAY, true)
          // 是否开启 TCP 底层心跳机制
          .childOption(ChannelOption.SO_KEEPALIVE, true)
          //表示系统用于临时存放已完成三次握手的请求的队列的最大长度,如果连接建立频繁，服务器处理创建新连接较慢，可以适当调大这个参数
          .option(ChannelOption.SO_BACKLOG, 128)
          .handler(new LoggingHandler(LogLevel.INFO))
          // 当客户端第一次进行请求的时候才会进行初始化
          .childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) {
              // 30 秒之内没有收到客户端请求的话就关闭连接
              ChannelPipeline p = ch.pipeline();
              p.addLast(new IdleStateHandler(30, 0, 0, TimeUnit.SECONDS));
              p.addLast(new RpcMessageEncoder());
              p.addLast(new RpcMessageDecoder());
              p.addLast(serviceHandlerGroup, new NettyRpcServerHandler());
            }
          });

      // 绑定端口，同步等待绑定成功
      ChannelFuture f = b.bind(host, PORT).sync();
      // 等待服务端监听端口关闭
      f.channel().closeFuture().sync();
    } catch (InterruptedException e) {
      log.error("occur exception when start server:", e);
    } finally {
      log.error("shutdown bossGroup and workerGroup");
      bossGroup.shutdownGracefully();
      workerGroup.shutdownGracefully();
      serviceHandlerGroup.shutdownGracefully();
    }
  }


}
